package main

import (
	"fmt"
	"log"
	"strings"
	"sync"

	"github.com/valyala/fasthttp"
)

type Server struct {
	mu       sync.RWMutex
	tables   map[string]Store
	basePath string
}

func (srv *Server) Setup() error {
	srv.tables = make(map[string]Store)
	db, err := NewStore(srv.basePath, *dbname)
	if err != nil {
		return err
	}
	srv.tables[*dbname] = db
	return nil
}

func (srv *Server) Handle(ctx *fasthttp.RequestCtx) {
	ret := NewResult()
	addr, source, state := parseArgs(ctx)
	if ctx.IsGet() {
		ret.WithAction(GET)
		log.Println("handle-get: " + addr + ", " + source + ", " + state)
		log.Println("path:" + string(ctx.Path()))
		path := strings.ToLower(string(ctx.Path()))
		if v, err := srv.get(addr, source, state, path); err == nil {
			ret.WithValue(v)
		} else {
			ret.WithError(err)
		}
	} else if ctx.IsPost() {
		ret.WithAction(POST)
		log.Println("handle-post: " + addr + ", " + source + ", " + state)
		if err := srv.put(addr, source, state); err == nil {
			ret.WithValue(nil)
		} else {
			ret.WithError(err)
		}
	} else {
		ret.WithError(ActionNotSupportError)
	}

	if ret.err != nil {
		if ret.err == Status404NotFoundErr {
			ret.WithCode(fasthttp.StatusNotFound)
		} else if isParamError(ret.err) {
			ret.WithCode(fasthttp.StatusBadRequest)
		} else {
			ret.WithCode(fasthttp.StatusInternalServerError)
		}
	}
	if ret.code == fasthttp.StatusOK {
		ctx.Success("application/json", ret.Finish().bytes())
	} else {
		ctx.Error(ret.Finish().String(), ret.code)
	}
	log.Printf("Response status: %d\n", ctx.Response.StatusCode())
	//log.Println(ctx.Response.StatusCode())
}

func parseArgs(ctx *fasthttp.RequestCtx) (addr, source, state string) {
	addr = ctx.RemoteIP().String()
	if ctx.IsGet() {
		source = string(ctx.QueryArgs().Peek("source"))
		state = string(ctx.QueryArgs().Peek("state"))
	} else if ctx.IsPost() {
		source = string(ctx.PostArgs().Peek("source"))
		state = string(ctx.PostArgs().Peek("state"))
	}
	return
}

func (srv *Server) put(addr, source, state string) error {
	srv.mu.Lock()
	defer srv.mu.Unlock()
	if addr != "" && source != "" && state != "" {
		return srv.tables[*dbname].Put(addr, source, state)
	} else {
		log.Println("Err: Request Parameters Error")
		return ReqParametersError
	}
}

func (srv *Server) get(addr, source, state, path string) ([]byte, error) {
	srv.mu.Lock()
	defer srv.mu.Unlock()
	switch path {
	case "/addr":
		if addr != "" {
			return srv.tables[*dbname].GetSources(addr)
		} else {
			log.Println("Err: Request Parameters Error")
			return nil, ReqParametersError
		}
	case "/addr/source":
		if addr != "" && source != "" {
			return srv.tables[*dbname].GetStates(addr, source)
		} else {
			log.Println("Err: Request Parameters Error")
			return nil, ReqParametersError
		}
	case "/addr/source/state":
		if addr != "" && source != "" && state != "" {
			return srv.tables[*dbname].GetTimes(addr, source, state)
		} else {
			log.Println("Err: Request Parameters Error")
			return nil, ReqParametersError
		}
	default:
		log.Println("Err: Request Path Error")
		return nil, RequestPathError
	}

}

func isParamError(err error) bool {
	return err == ActionNotSupportError || err == RequestPathError ||
		err == ReqParametersError
}

var ActionNotSupportError = fmt.Errorf("ActionNotSupport")
var RequestPathError = fmt.Errorf("RequestPathError")
var ReqParametersError = fmt.Errorf("ReqParametersError")
var Status404NotFoundErr = fmt.Errorf("404NotFound")
